---
title: XState
description: This doc describes XState extension.
nav: 4.05
keywords: xstate,machine,atomwithmachine
---

Jotai's state management is primitive and flexible,
but that sometimes means too free.
XState is a sophisticated library to provide
a better and safer abstraction for state management.

### Install

You have to install `xstate` and `jotai-xstate` to use this feature.

```
npm i xstate jotai-xstate
```

## atomWithMachine

`atomWithMachine` creates a new atom with XState machine.
It receives a function `getMachine` to create a new machine.
`getMachine` is invoked at the first use with `get` argument,
with which you can read other atom values.

```tsx
import { useAtom } from 'jotai'
import { atomWithMachine } from 'jotai-xstate'
import { assign, createMachine } from 'xstate'

const createEditableMachine = (value: string) =>
  createMachine<{ value: string }>({
    id: 'editable',
    initial: 'reading',
    context: {
      value,
    },
    states: {
      reading: {
        on: {
          dblclick: 'editing',
        },
      },
      editing: {
        on: {
          cancel: 'reading',
          commit: {
            target: 'reading',
            actions: assign({
              value: (_, { value }) => value,
            }),
          },
        },
      },
    },
  })

const defaultTextAtom = atom('edit me')
const editableMachineAtom = atomWithMachine((get) =>
  // `get` is available only for initializing a machine
  createEditableMachine(get(defaultTextAtom)),
)

const Toggle = () => {
  const [state, send] = useAtom(editableMachineAtom)

  return (
    <div>
      {state.matches('reading') && (
        <strong onDoubleClick={send}>{state.context.value}</strong>
      )}
      {state.matches('editing') && (
        <input
          autoFocus
          defaultValue={state.context.value}
          onBlur={(e) => send({ type: 'commit', value: e.target.value })}
          onKeyDown={(e) => {
            if (e.key === 'Enter') {
              send({ type: 'commit', value: e.target.value })
            }
            if (e.key === 'Escape') {
              send('cancel')
            }
          }}
        />
      )}
      <br />
      <br />
      <div>
        Double-click to edit. Blur the input or press <code>enter</code> to
        commit. Press <code>esc</code> to cancel.
      </div>
    </div>
  )
}
```

### Restartable machine stored in a global Provider (provider-less mode)

When your machine reaches its final state it cannot receive any more events.
If your atomWithMachine is initialized in global store (aka provider-less mode),
to restart it you need to send a `RESTART` event to your machine like so:

```tsx
import { RESTART } from 'jotai-xstate'

const YourComponent = () => {
  const [current, send] = useAtom(yourMachineAtom)

  const isFinalState = current.matches('myFinalState')

  useEffect(() => {
    // restart globally initialized machine on component unmount
    return () => {
      if (isFinalState) send(RESTART)
    }
  }, [isFinalState])
}
```

### Examples

Check examples with atomWithMachine:

<CodeSandbox id="fxtoe3" />

Restartable machine:

<CodeSandbox id="n179xd" />

### Tutorials

Check out a course about Jotai and XState.

[Complex State Management in React with Jotai and XState](https://egghead.io/courses/complex-state-management-in-react-with-jotai-and-xstate-3be0a740)

(Note: In the course, it uses `jotai/xstate` which is supersede by `jotai-xstate`.)
